package sg.nus.iss.vmcs.gui;

import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Dialog;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.Label;
import java.awt.Panel;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import sg.nus.iss.vmcs.controllers.TransactionController;
import sg.nus.iss.vmcs.gui.elements.CoinInputBox;
import sg.nus.iss.vmcs.gui.elements.DrinkSelectionItemStoreViewerBuilder;
import sg.nus.iss.vmcs.gui.elements.FlowItemDisplayStrategy;
import sg.nus.iss.vmcs.gui.elements.LabelledDisplay;
import sg.nus.iss.vmcs.gui.elements.StoreViewer;
import sg.nus.iss.vmcs.gui.elements.VariableDisplayState;
import sg.nus.iss.vmcs.gui.elements.WarningDisplay;
import sg.nus.iss.vmcs.guilisteners.DrinkSelectionListener;
import sg.nus.iss.vmcs.guilisteners.TerminateButtonListener;
import sg.nus.iss.vmcs.utils.Constant;
import sg.nus.iss.vmcs.utils.MoneyFormatter;
import sg.nus.iss.vmcs.utils.StoreType;

/**
 * Customer UI for simulation of customer functions<br>
 * @author Philip
 */
public class CustomerPanel extends Dialog implements GUIChangeObserver{

	/** Generated serial ID */
	private static final long serialVersionUID = 6942101547474188460L;
	
	/** Controller for the Customer Panel */
	private TransactionController transCtrl;

	/** Button for terminating a transaction */
	private Button btnTerminateTrans;
	
	/** Coin Input Box used to simulate the customer's coin inputs */
	private CoinInputBox coinInputBox;
	
	/** Drink Selection Box used for drink selections */
	private StoreViewer drinkSelBox;
	
	/** Labelled Display to represent Coin Collection */
	private LabelledDisplay ldCollectCoin;
	
	/** Labelled Display to represent Can Collection */
	private LabelledDisplay ldCollectCan;
	
	/** Labelled Display to show total money inserted */
	private LabelledDisplay ldTotalMoneyInserted;
	
	/** Warning display for invalid coin situations */
	private WarningDisplay wdInvalidCoin;
	
	/** Warning display for no change available situations */
	private WarningDisplay wdNoChangeAvail;
	
	/** Holds the current change status */
	private boolean curChangeStatus = true;
	
	/** Holds the invalid coin status */
	private boolean isCoinInvalid = false;
	
	/**
	 * Constructor for the Customer 
	 * @param fr
	 * @param transCtrl
	 */
	public CustomerPanel(Frame fr, TransactionController transCtrl) {
		super(fr, Constant.CUST_BANNER);
		this.transCtrl = transCtrl;
		
		//Banner
		Panel pnlBanner = new Panel();
		Label lblBanner = new Label(Constant.CUST_BANNER);
		lblBanner.setFont(Constant.BANNER_FONT);
		pnlBanner.add(lblBanner);
		
		//Coin Input Box
		coinInputBox = new CoinInputBox(transCtrl.getCoinReceiver());
		coinInputBox.setActive(false);
		
		//Invalid Coin Warning Display
		wdInvalidCoin = new WarningDisplay(Constant.CUST_INVALID_COIN);
		wdInvalidCoin.setVariableDisplayState(VariableDisplayState.getDisplayState(VariableDisplayState.Type.WarningOff));
		wdInvalidCoin.setDisplay();
		
		//Total Money Inserted Labelled Display
		ldTotalMoneyInserted = new LabelledDisplay(Constant.CUST_TOTAL_MONEY, LabelledDisplay.DEFAULT, new FlowItemDisplayStrategy());
		ldTotalMoneyInserted.setEditable(false);
		
		//Drink Selection Box
		DrinkSelectionItemStoreViewerBuilder builder = 
			new DrinkSelectionItemStoreViewerBuilder(
					transCtrl.getMainController().getStoreController().getStore(StoreType.DRINKS),
					new DrinkSelectionListener(transCtrl));
		
		builder.createNewStoreViewer();
		builder.buildStoreViewerItems();
		builder.buildListeners();
		
		drinkSelBox = builder.getStoreViewer();
		drinkSelBox.setEnabled(true);
		
		//No Change Warning Display
		wdNoChangeAvail = new WarningDisplay(Constant.CUST_NO_CHANGE);
		wdNoChangeAvail.setVariableDisplayState(VariableDisplayState.getDisplayState(VariableDisplayState.Type.WarningOff));
		wdNoChangeAvail.setDisplay();
		
		//Terminate Button
		Panel pnlTerminateButton = new Panel();
		btnTerminateTrans = new Button(Constant.CUST_TERMINATE);
		btnTerminateTrans.addActionListener(new TerminateButtonListener(transCtrl));
		btnTerminateTrans.setEnabled(false);
		pnlTerminateButton.add(btnTerminateTrans);
		
		//Collect Coins Labelled Display
		ldCollectCoin = new LabelledDisplay(Constant.CUST_COLLECT_CHANGE, LabelledDisplay.DEFAULT, new FlowItemDisplayStrategy());
		ldCollectCoin.setEditable(false);
		ldCollectCoin.setValue(MoneyFormatter.toMoney(0));
		
		//Collect Can Labelled Display
		ldCollectCan = new LabelledDisplay(Constant.CUST_COLLECT_PROD, 15, new FlowItemDisplayStrategy());
		ldCollectCan.setEditable(false);
		ldCollectCan.setValue("No Can");
		
		//North Panel Creation
		Panel pnlNorth = new Panel();
		pnlNorth.setLayout(new GridLayout(4, 1));
		pnlNorth.add(pnlBanner);
		pnlNorth.add(coinInputBox);
		pnlNorth.add(wdInvalidCoin);
		pnlNorth.add(ldTotalMoneyInserted);
		
		//South Panel Creation
		Panel pnlSouth = new Panel();
		pnlSouth.setLayout(new GridLayout(4, 1));
		pnlSouth.add(wdNoChangeAvail);
		pnlSouth.add(pnlTerminateButton);
		pnlSouth.add(ldCollectCoin);
		pnlSouth.add(ldCollectCan);
		
		//Setting Layouts
		this.setLayout(new BorderLayout());
		this.add(pnlNorth, BorderLayout.NORTH);
		this.add(drinkSelBox, BorderLayout.CENTER);
		this.add(pnlSouth, BorderLayout.SOUTH);
		this.setResizable(false);
		this.pack();
		addWindowAdapter();
		
		//Register Events
		GUIChangeManager.getInstance().registerEvent(GUIChangeManagerEvent.SetChange, 		this);
		GUIChangeManager.getInstance().registerEvent(GUIChangeManagerEvent.SetCan, 			this);
		GUIChangeManager.getInstance().registerEvent(GUIChangeManagerEvent.ResetCoinsInserted, 	this);
		GUIChangeManager.getInstance().registerEvent(GUIChangeManagerEvent.SetChangeStatus, 	this);
		GUIChangeManager.getInstance().registerEvent(GUIChangeManagerEvent.AllowCanSelection, 	this);
		GUIChangeManager.getInstance().registerEvent(GUIChangeManagerEvent.AllowCoinReceive, 	this);
		GUIChangeManager.getInstance().registerEvent(GUIChangeManagerEvent.InvalidCoinInserted, this);
		GUIChangeManager.getInstance().registerEvent(GUIChangeManagerEvent.ValidCoinInserted, 	this);
		GUIChangeManager.getInstance().registerEvent(GUIChangeManagerEvent.AllowTerminate, 	this);
	}

	/**
	 * Displays the Customer Panel
	 */
	public void display() {
		this.setVisible(true);
	}
	
	/**
	 * Removes the Customer Panel from being displayed
	 */
	public void closeDown() {
		this.dispose();
	}

	/* 
	 * (When notified that an event has occured, reacts to the event directly
	 * @param event GUIChangeManagerEvent Type
	 */
	@Override
	public void notifyEvent(GUIChangeManagerEvent event) {
		switch(event) {
			case SetChange:
				ldCollectCoin.setValue("");
				break;
			case SetCan:
				ldCollectCan.setValue("");
				break;
			case ResetCoinsInserted:
				ldTotalMoneyInserted.setValue("");
				break;
		}
	}

	/* 
	 * (When notified that an event has occured, reacts to the event directly
	 * @param event GUIChangeManagerEvent Type
	 * @param params Any additional configurations
	 */
	@Override
	public void notifyEvent(GUIChangeManagerEvent event, Object... params) {
		switch(event) {
			case SetChangeStatus:
				if(params.length > 0 && params[0] instanceof Boolean) 
					setChangeStatus((Boolean)params[0]);
				break;
			case AllowCanSelection:
				if(params.length > 0 && params[0] instanceof Boolean) 
					drinkSelBox.setActive((Boolean)params[0]);
				break;
			case AllowCoinReceive:
				if(params.length > 0 && params[0] instanceof Boolean) 
					coinInputBox.setActive((Boolean)params[0]);
				break;
			case InvalidCoinInserted:
				if(params.length > 0 && params[0] instanceof Boolean)
					setInvalidCoinStatus((Boolean)params[0]);
				break;
			case ValidCoinInserted:
				if(params.length > 0 && params[0] instanceof Integer) 
					ldTotalMoneyInserted.setValue((Integer)params[0]);
				break;
			case SetChange:
				if(params.length > 0 && params[0] instanceof String)
					ldCollectCoin.setValue((String)params[0]);
				break;
			case SetCan:
				if(params.length > 0 && params[0] instanceof String)
					ldCollectCan.setValue((String)params[0]);
				break;	
			case AllowTerminate:
				if(params.length > 0 && params[0] instanceof Boolean) 
					setTerminate((Boolean)params[0]);
				break;
		}	
	}
		
	/**
	 * Sets the terminate
	 * @param newStatus new status of change
	 */
	private void setTerminate(boolean newStatus) {
		btnTerminateTrans.setEnabled(newStatus);
	}
	
	/**
	 * Sets the change status display
	 * @param newStatus new status of change
	 */
	private void setChangeStatus(boolean newStatus) {
		if((! curChangeStatus && newStatus) || (curChangeStatus && !newStatus)) {
			wdNoChangeAvail.setDisplay();
		}
		
		curChangeStatus = newStatus;
	}
	
	/**
	 * Sets the change status display
	 * @param newStatus new status of change
	 */
	private void setInvalidCoinStatus(boolean newStatus) {
		if((! isCoinInvalid && newStatus) || (isCoinInvalid && !newStatus)) {
			wdInvalidCoin.setDisplay();
		}
		
		isCoinInvalid = newStatus;
	}
	
	/**
	 * Adds a window adapter to close down the system
	 */
	private void addWindowAdapter() {
		this.addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				transCtrl.terminateTransaction();
				closeDown();
				transCtrl.getMainController().getSimulatorControlPanel().setActive(SimulatorControlPanel.ACT_CUSTOMER, true);
			}
		});
	}
}
